package br.poli.ecomp.sma.massim.arch;

import java.util.logging.Logger;

/** 
 *  An agent architecture that try to identify a crashed agent and then try to fix it.
 * 
 *  @author Joabe Jesus
 */
public class AgentCrashThread extends Thread {
    
    private EcompArch arch;
    
    private Logger logger;

    private long maxCycleTime = 4000;
    private boolean didPercept = true;
    private boolean dead = false;

    private Thread agThread = null;

	public AgentCrashThread(EcompArch arch, Logger logger) {
		if (arch == null || logger == null)
			throw new IllegalArgumentException();
		this.arch = arch;
		this.logger = logger;
    }

	public void setMaxCycleTime(long deadline) {
		if (deadline < 0)
			throw new IllegalArgumentException();

		maxCycleTime = deadline;
	}
    
    protected synchronized void didPerceive() {
    	agThread = Thread.currentThread();
        didPercept = true;
        notifyAll();
    }
    
    private synchronized void waitPerceive() throws InterruptedException {
        wait(maxCycleTime);
    }

    private boolean isCrashed() {
        return !arch.isPerceiving() && !didPercept;
    }
    
    /**
     * Try to fix the agent.
     * 
     * Approach 1: simulates the agent has stopped
     */
    protected void fixReasoning() throws Exception {
        logger.warning("I am dead! Trying to fix...");
        
        // simulates a stop running
        dead = true;

        // gives some time to receive a perception
        waitPerceive();
        try {
        	if (isCrashed())
        		fixReasoningThread();
        } finally {
            // start the agent again
            dead = false;
        }
    }

    /**
     * Try to fix the agent.
     * 
     * Approach 2: interrupt the agent thread
     */
    protected void fixReasoningThread() throws Exception {
        if (agThread != null) {
            logger.warning("I am still dead! Interrupting the reasoning thread...");

            // try to interrupt the agent thread.
            agThread.interrupt();
        
            waitPerceive();
            if (!isCrashed())
                return;

            logger.warning("Interrupt doesn't work!");
        }

        logger.warning("I am still dead! Trying to fix the architecture...");
        arch.fix();
    }
    
	@Override
	public void run() {
		try {
			sleep(maxCycleTime * 2); // wait a bit before start checking
			while (!dead && arch.isRunning()) {
				didPercept = false;
				sleep(maxCycleTime);
				if (isCrashed()) {
					fixReasoning();
				}
			}
		} catch (Exception e) {
			// no problem the agent should stop running, simply quite the thread
		}
	}
}
